iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 3
1
自我挑戰組

玩轉 React 從0到1系列 第 6

【Day 6】關於ES6作用域

  • 分享至 

  • xImage
  •  

前言

在 ES6 之前,唯一產生作用域 (Scope) 的方法就是 函式(function);在 ES6 後,則多了一種方式 區塊(Block)。以函式來說,每一個函式內部有自己的作用域,出了作用域就無法存取這個函式內部所定義的變數;而區塊則是可以使用{...}定義區塊範圍,以 const 和 let 宣告區塊內作用域的變數。

說到這裡就要問,什麼是作用域 (Scope) ?

作用域規定了如何去找尋變量,也就是確定當前取得變量的權限

Javascript 採取 靜態作用域(lexical scope)

靜態作用域 (lexical scope)

變數的作用在語法解析的時候,就已經先確定作用域了,而且不會再改變

// global scope
function getUserId() {
    // function scope
    var user_id = '111';
    return function () {
      // function scope
      console.log(user_id);
    }
}

let showUserId = getUserId()
function getUser() {
    // function scope
    var user_id = '999';
    showUserId()
}

console.log(showUserId()) // 111
getUser() // 111

當程式執行到getUserId內部的 console.log(user_id) 時,無論此作用域是怎麼調用,都是通過向上找到var user_id = '111'而獲取 user_id 的值。所以在函數還未執行前,就確定可以根據 static scope找到變量相對應的值,而這就是靜態作用域。

函式作用域 (Function Scope)

function ScopeTest(a) {
  var b = 4;
  function bar() {
    console.log(`a:${a},b:${b},c:${c}`); // "a:3,b:4,c:5"
  }
  var c = 5;
  console.log(a); // 3
  console.log(b); // 4
  console.log(c); // 5
  bar();
}

ScopeTest(3);
console.log(a); // ReferenceError

在全域的狀況下,這裡會發現,此時是無法存取 a 這個在functionScopeTest裡的變數的;可是 bar 卻可以存取 functionScopeTest裡的 a, b, c變數,這是因為他們都存在在同一個 function 裡 = 同一個作用域的原因。

簡而言之,就是內層可以存取到外層的東西,但是外層存不到內層的。

區塊作用域 (Block Scope)

在 ES6 出現之前,JS並沒有很嚴謹的定義 block scope,除了不推薦使用的 eval 和 with,大概只有 try/catch 的 catch 擁有區塊的概念。而 ES6 的發布明確帶來兩個新的變量:let 和 const,而這是推薦作用於 區塊作用域 的。

// global scope
function showUserId() {
    // function scope
    var user_id = '111'
    
    if (true) {
        // block scope
        let user_id_2 = '999'
        const user_id_3 = '777'
        console.log(user_id_3) // 777
    }
    
    console.log(user_id) // 111
    console.log(user_id_2) // ReferenceError
}
showUserId()

在區塊之外,可以發現 console.log 是無法存取 user_id_2user_id_3 的,而就是 區塊作用域 的限制。
順帶一提,Javascript 是一個會自帶垃圾回收(Garbage collection)機制的語言,一般來說 JS 會自動幫我們處理好,但是我們也可以透過 區塊作用域 提升垃圾回收的效率。

函式作用域 VS 區塊作用域

雖然我們在第二天已經有稍微說明 var 在 function 作用域上的問題,但今天就讓我們講的再更詳細一點吧?

for(var i = 0; i < 10; i++){
    setTimeout(function(){
        console.log(i);
    },100);
}   // 10,10,10,10,......
for(let i = 0; i < 10; i++){
    setTimeout(function(){
        console.log(i);
    },100);
}   // 1,2,3 4,5,6...

這裡說明了 varlet 是不同的,var聲明變量會提升,是基於 global scope 或 function scope 中的,在程式執行setTimeout的時候for運行已經結束了,所以值都會是 10。而 let 是基於 block scope的,每次循環 i 只在當前的 block 有效,所以值是正確的。

結論

  • 介紹了 靜態作用域
  • 介紹了 函式作用域
  • 介紹了 區塊作用域

/images/emoticon/emoticon14.gif預計會在補入閉包跟 hoisting 概念

參考

JS 作用域
我知道你懂 hoisting,可是你了解到多深?


上一篇
【Day 5】關於ES6從類別到繼承
下一篇
【Day 7】關於ES6 模組系統
系列文
玩轉 React 從0到130
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言